Este cuaderno desarrolla el análisis estadístico inferencial de datos del sector turístico, en el marco de la Actividad 3. Se abordan dos preguntas:
La variable satisfaction se mide en escala
Likert de 1 a 5. Aunque se trata de una escala ordinal, se
considera cuasi-continua y se trata como numérica, lo cual permite
aplicar técnicas como t-test y análisis de correlación, tal como se
justifica en estudios previos (Joshi et al., 2015).
El dataset original se encuentra en Kaggle: Cultural Tourism Dataset. Contiene registros simulados sobre turistas, preferencias, rutas, accesibilidad y evaluación de servicios como realidad virtual (VR).
También se incluirán análisis exploratorios, transformaciones y visualizaciones intermedias para enriquecer las conclusiones.
options(scipen = 999) # Desactiva la notación científica
Se utiliza pacman para instalar y cargar de forma más
eficiente múltiples paquetes necesarios. Esta librería evita errores por
duplicidad y permite mantener el código limpio.
if (!require("pacman")) install.packages("pacman")
pacman::p_load(
tidyverse, # Manipulación y visualización de datos
skimr, # Resumen estadístico completo con histogramas
kableExtra, # Tablas HTML más estéticas
janitor, # Limpieza de nombres de columnas
modeest, # Cálculo de la moda
raster, # Cálculo del coeficiente de variación
moments, # Asimetría y curtosis
ggcorrplot, # Mapa de calor de correlaciones
corrplot, # Mapa de calor de correlaciones
readr, # Lectura de CSV
ggplot2, # Gráficos
dplyr, # Manipulación de datos
stringr, # Operaciones con texto (útil para limpiar intereses)
moderndive,
nycflights13
)
ℹ️ Evitamos conflictos con
library()yrequire().
# Cargar el dataset directamente desde GitHub
url_datos <- "https://raw.githubusercontent.com/ea-analisisdatos/unir/refs/heads/main/datos/tourism_dataset_5000.csv"
datos <- read_csv(url_datos)
head(datos, 5) # mostra las primeras 5 filas del dataframe
ℹ️ Observa también que no se muestran todas las columnas. Puedes pulsar el ícono de “flecha ▶️” en la esquina superior derecha de la tabla para explorarlas.
# Verificar nombres de columnas
columnas <- colnames(datos)
columnas
## [1] "Tourist ID" "Age" "Interests" "Preferred Tour Duration" "Accessibility"
## [6] "Site Name" "Sites Visited" "Tour Duration" "Route ID" "Tourist Rating"
## [11] "System Response Time" "Recommendation Accuracy" "VR Experience Quality" "Satisfaction"
ℹ️ Los nombres de columnas incluyen espacios, lo que puede dificultar el trabajo en R.
Este paso, janitor convierte los nombres compuestos de
las columnas en formato snake_case, sin espacios ni símbolos,
facilitando su uso en R.
datos <- janitor::clean_names(datos)
head(datos, 5)
ℹ️ Puedes pulsar el ícono de “flecha ▶️” en la esquina superior derecha de la tabla HTML para explorarlas.
# Verificar nombres de columnas despues de la limpieza
columnas <- colnames(datos)
columnas
## [1] "tourist_id" "age" "interests" "preferred_tour_duration" "accessibility"
## [6] "site_name" "sites_visited" "tour_duration" "route_id" "tourist_rating"
## [11] "system_response_time" "recommendation_accuracy" "vr_experience_quality" "satisfaction"
route_id a factor (variable categórica)datos <- datos %>%
mutate(route_id = as.factor(route_id))
tourist_id a factor (variable categórica)datos <- datos %>%
mutate(tourist_id = as.factor(tourist_id))
ℹ️ Aunque tienen valores numéricos,
tourist_idyroute_idson identificadores sin sentido ordinal. Por tanto, deben tratarse como factores.
# Limpiar caracteres especiales y descomponer en filas
intereses_limpios <- datos %>%
mutate(interests = str_replace_all(interests, "\\[|\\]|‘|’|'|\"", "")) %>% # elimina caracteres extraños
separate_rows(interests, sep = ",\\s*") %>% # divide por comas
mutate(interests = str_trim(interests)) %>% # quita espacios
count(interests, sort = TRUE) # cuenta ocurrencias
ℹ️ Comentario técnico:
separate_rows()descompone listas multivaluadas creando una nueva fila por cada elemento de interés individual. El dataframe final muestra una fila por cada categoría de interés única con su frecuencia total consolidada.
Aunque varias variables del dataset están codificadas como numéricas, en realidad representan escalas ordinales, como ocurre con:
satisfaction: Satisfacción general (escala Likert
1–5)tourist_rating: Valoración del turista (escala Likert
1–5)vr_experience_quality: Calidad de la experiencia VR
(escala Likert 1–5)recommendation_accuracy: Precisión del sistema (0–100,
asimilable a escala continua)preferred_tour_duration: Preferencia en duración de
tours (en días)Estas variables mantienen un orden lógico entre
categorías, pero no necesariamente equidistante. Sin embargo,
en análisis estadísticos como:
- Test t de Student (para comparar medias)
- Correlaciones de Pearson (entre variables)
… es común y aceptado tratar escalas ordinales con más de 3
categorías como cuantitativas (de intervalo),
especialmente si están bien distribuidas.
- En este estudio, se tratarán como numéricas para aprovechar los métodos inferenciales, pero se dejará constancia de su naturaleza ordinal.
- Esto se justifica ampliamente en literatura estadística aplicada (Boone & Boone, 2012; Joshi et al., 2015) cuando se asume que las distancias entre valores en escalas ordinales son equidistantes y se desea realizar análisis paramétricos.
# Cómo están estructurados los datos internamente
glimpse(datos)
## Rows: 5,000
## Columns: 14
## $ tourist_id [3m[38;5;246m<fct>[39m[23m 1[38;5;246m, [39m2[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m5[38;5;246m, [39m6[38;5;246m, [39m7[38;5;246m, [39m8[38;5;246m, [39m9[38;5;246m, [39m10[38;5;246m, [39m11[38;5;246m, [39m12[38;5;246m, [39m13[38;5;246m, [39m14[38;5;246m, [39m15[38;5;246m, [39m16[38;5;246m, [39m17[38;5;246m, [39m18[38;5;246m, [39m19[38;5;246m, [39m20[38;5;246m, [39m21[38;5;246m, [39m22[38;5;246m, [39m23[38;5;246m, [39m24[38;5;246m, [39m25[38;5;246m, [39m26[38;5;246m, [39m27[38;5;246m, [39m28[38;5;246m, [39m29[38;5;246m, [39m30[38;5;246m, [39m3…
## $ age [3m[38;5;246m<dbl>[39m[23m 48[38;5;246m, [39m37[38;5;246m, [39m43[38;5;246m, [39m46[38;5;246m, [39m53[38;5;246m, [39m50[38;5;246m, [39m61[38;5;246m, [39m23[38;5;246m, [39m34[38;5;246m, [39m61[38;5;246m, [39m27[38;5;246m, [39m37[38;5;246m, [39m45[38;5;246m, [39m61[38;5;246m, [39m60[38;5;246m, [39m50[38;5;246m, [39m59[38;5;246m, [39m18[38;5;246m, [39m24[38;5;246m, [39m63[38;5;246m, [39m45[38;5;246m, [39m62[38;5;246m, [39m65[38;5;246m, [39m53[38;5;246m, [39m26[38;5;246m, [39m63[38;5;246m, [39m24[38;5;246m, [39m20[38;5;246m, [39m…
## $ interests [3m[38;5;246m<chr>[39m[23m "['Architecture', 'Art', 'History']"[38;5;246m, [39m"['Cultural', 'Nature']"[38;5;246m, [39m"['History', 'Art', 'Architecture']"[38;5;246m, [39m"['Cultura…
## $ preferred_tour_duration [3m[38;5;246m<dbl>[39m[23m 5[38;5;246m, [39m6[38;5;246m, [39m6[38;5;246m, [39m8[38;5;246m, [39m5[38;5;246m, [39m4[38;5;246m, [39m7[38;5;246m, [39m5[38;5;246m, [39m5[38;5;246m, [39m5[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m8[38;5;246m, [39m8[38;5;246m, [39m4[38;5;246m, [39m7[38;5;246m, [39m7[38;5;246m, [39m6[38;5;246m, [39m5[38;5;246m, [39m8[38;5;246m, [39m6[38;5;246m, [39m7[38;5;246m, [39m8[38;5;246m, [39m6[38;5;246m, [39m8[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m5[38;5;246m, [39m7[38;5;246m, [39m6[38;5;246m, [39m7[38;5;246m, [39m6[38;5;246m, [39m8[38;5;246m, [39m5[38;5;246m, [39m3[38;5;246m, [39m7[38;5;246m, [39m6[38;5;246m, [39m4…
## $ accessibility [3m[38;5;246m<lgl>[39m[23m FALSE[38;5;246m, [39mFALSE[38;5;246m, [39mTRUE[38;5;246m, [39mFALSE[38;5;246m, [39mTRUE[38;5;246m, [39mFALSE[38;5;246m, [39mFALSE[38;5;246m, [39mFALSE[38;5;246m, [39mTRUE[38;5;246m, [39mFALSE[38;5;246m, [39mFALSE[38;5;246m, [39mTRUE[38;5;246m, [39mTRUE[38;5;246m, [39mTRUE[38;5;246m, [39mTRUE[38;5;246m, [39mFALSE[38;5;246m, [39mTRUE[38;5;246m, [39mT…
## $ site_name [3m[38;5;246m<chr>[39m[23m "Eiffel Tower"[38;5;246m, [39m"Colosseum"[38;5;246m, [39m"Machu Picchu"[38;5;246m, [39m"Colosseum"[38;5;246m, [39m"Colosseum"[38;5;246m, [39m"Machu Picchu"[38;5;246m, [39m"Eiffel Tower"[38;5;246m, [39m"Eiffel T…
## $ sites_visited [3m[38;5;246m<chr>[39m[23m "['Eiffel Tower', 'Great Wall of China', 'Taj Mahal']"[38;5;246m, [39m"['Great Wall of China']"[38;5;246m, [39m"['Eiffel Tower']"[38;5;246m, [39m"['Machu …
## $ tour_duration [3m[38;5;246m<dbl>[39m[23m 7[38;5;246m, [39m1[38;5;246m, [39m2[38;5;246m, [39m5[38;5;246m, [39m7[38;5;246m, [39m4[38;5;246m, [39m6[38;5;246m, [39m2[38;5;246m, [39m6[38;5;246m, [39m6[38;5;246m, [39m6[38;5;246m, [39m5[38;5;246m, [39m6[38;5;246m, [39m1[38;5;246m, [39m6[38;5;246m, [39m8[38;5;246m, [39m3[38;5;246m, [39m6[38;5;246m, [39m6[38;5;246m, [39m2[38;5;246m, [39m2[38;5;246m, [39m5[38;5;246m, [39m3[38;5;246m, [39m6[38;5;246m, [39m3[38;5;246m, [39m6[38;5;246m, [39m4[38;5;246m, [39m4[38;5;246m, [39m8[38;5;246m, [39m2[38;5;246m, [39m4[38;5;246m, [39m2[38;5;246m, [39m4[38;5;246m, [39m1[38;5;246m, [39m7[38;5;246m, [39m4[38;5;246m, [39m4[38;5;246m, [39m2…
## $ route_id [3m[38;5;246m<fct>[39m[23m 1000[38;5;246m, [39m2000[38;5;246m, [39m3000[38;5;246m, [39m4000[38;5;246m, [39m5000[38;5;246m, [39m6000[38;5;246m, [39m7000[38;5;246m, [39m8000[38;5;246m, [39m9000[38;5;246m, [39m10000[38;5;246m, [39m11000[38;5;246m, [39m12000[38;5;246m, [39m13000[38;5;246m, [39m14000[38;5;246m, [39m15000[38;5;246m, [39m16000[38;5;246m, [39m17000[38;5;246m, [39m18…
## $ tourist_rating [3m[38;5;246m<dbl>[39m[23m 1.6[38;5;246m, [39m2.6[38;5;246m, [39m1.7[38;5;246m, [39m2.0[38;5;246m, [39m3.7[38;5;246m, [39m2.2[38;5;246m, [39m4.1[38;5;246m, [39m2.8[38;5;246m, [39m4.0[38;5;246m, [39m3.8[38;5;246m, [39m3.2[38;5;246m, [39m1.7[38;5;246m, [39m1.8[38;5;246m, [39m4.3[38;5;246m, [39m2.9[38;5;246m, [39m1.2[38;5;246m, [39m2.1[38;5;246m, [39m2.7[38;5;246m, [39m2.6[38;5;246m, [39m2.7[38;5;246m, [39m2.8[38;5;246m, [39m3.3[38;5;246m, [39m4.…
## $ system_response_time [3m[38;5;246m<dbl>[39m[23m 3.73[38;5;246m, [39m2.89[38;5;246m, [39m2.22[38;5;246m, [39m2.34[38;5;246m, [39m2.00[38;5;246m, [39m3.74[38;5;246m, [39m4.66[38;5;246m, [39m4.84[38;5;246m, [39m1.54[38;5;246m, [39m3.59[38;5;246m, [39m4.48[38;5;246m, [39m2.63[38;5;246m, [39m3.47[38;5;246m, [39m3.48[38;5;246m, [39m2.82[38;5;246m, [39m4.44[38;5;246m, [39m2.12[38;5;246m, [39m3.63[38;5;246m, [39m2.63…
## $ recommendation_accuracy [3m[38;5;246m<dbl>[39m[23m 97[38;5;246m, [39m90[38;5;246m, [39m94[38;5;246m, [39m92[38;5;246m, [39m96[38;5;246m, [39m99[38;5;246m, [39m93[38;5;246m, [39m91[38;5;246m, [39m95[38;5;246m, [39m95[38;5;246m, [39m99[38;5;246m, [39m100[38;5;246m, [39m100[38;5;246m, [39m91[38;5;246m, [39m94[38;5;246m, [39m91[38;5;246m, [39m95[38;5;246m, [39m100[38;5;246m, [39m86[38;5;246m, [39m92[38;5;246m, [39m94[38;5;246m, [39m96[38;5;246m, [39m88[38;5;246m, [39m97[38;5;246m, [39m90[38;5;246m, [39m93[38;5;246m, [39m87[38;5;246m, [39m9…
## $ vr_experience_quality [3m[38;5;246m<dbl>[39m[23m 4.5[38;5;246m, [39m4.5[38;5;246m, [39m4.7[38;5;246m, [39m4.7[38;5;246m, [39m4.8[38;5;246m, [39m4.5[38;5;246m, [39m4.5[38;5;246m, [39m4.9[38;5;246m, [39m4.4[38;5;246m, [39m4.7[38;5;246m, [39m4.2[38;5;246m, [39m4.6[38;5;246m, [39m4.5[38;5;246m, [39m4.4[38;5;246m, [39m4.2[38;5;246m, [39m4.2[38;5;246m, [39m4.3[38;5;246m, [39m4.4[38;5;246m, [39m4.7[38;5;246m, [39m4.8[38;5;246m, [39m4.7[38;5;246m, [39m4.0[38;5;246m, [39m5.…
## $ satisfaction [3m[38;5;246m<dbl>[39m[23m 3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m4[38;5;246m, [39m4[38;5;246m, [39m5[38;5;246m, [39m3[38;5;246m, [39m3[38;5;246m, [39m5[38;5;246m, [39m3[38;5;246m, [39m5[38;5;246m, [39m3[38;5;246m, [39m5…
ℹ️
glimpse()ofrece una descripción compacta: tipo de cada columna, primeras observaciones y estructuras.
# Tabla con tipos de datos
tabla_variables <- tibble(
Variable = names(datos),
Tipo = sapply(datos, function(x) class(x)[1])
)
kable(tabla_variables, format = "html", caption = "Tipos de variables") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))
| Variable | Tipo |
|---|---|
| tourist_id | factor |
| age | numeric |
| interests | character |
| preferred_tour_duration | numeric |
| accessibility | logical |
| site_name | character |
| sites_visited | character |
| tour_duration | numeric |
| route_id | factor |
| tourist_rating | numeric |
| system_response_time | numeric |
| recommendation_accuracy | numeric |
| vr_experience_quality | numeric |
| satisfaction | numeric |
Este diccionario ayuda a interpretar cada columna del dataset original. Algunas variables presentan valores de tipo ordinal, como escalas Likert de 1 a 5.
| Variable | Descripción |
|---|---|
| tourist_id | Identificador único del turista |
| age | Edad del turista |
| interests | Temas de interés |
| preferred_tour_duration | Duración preferida del tour (días) |
| accessibility | Requiere accesibilidad (TRUE/FALSE) |
| site_name | Nombre del sitio visitado |
| sites_visited | Lista de sitios visitados |
| tour_duration | Duración del tour realizado |
| route_id | ID de la ruta turística |
| tourist_rating | Valoración del turista (1-5) |
| system_response_time | Tiempo de respuesta del sistema |
| recommendation_accuracy | Precisión de recomendaciones (%) |
| vr_experience_quality | Calidad de experiencia VR (1-5) |
| satisfaction | Satisfacción general (1-5) |
skimr muestra tipo de variable, valores únicos, media,
desviación, y distribución en mini histograma. Ideal para descripción
exploratoria.
# Análisis descriptivo rápido
skim(datos)
| Name | datos |
| Number of rows | 5000 |
| Number of columns | 14 |
| _______________________ | |
| Column type frequency: | |
| character | 3 |
| factor | 2 |
| logical | 1 |
| numeric | 8 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| interests | 0 | 1 | 7 | 39 | 0 | 85 | 0 |
| site_name | 0 | 1 | 9 | 19 | 0 | 5 | 0 |
| sites_visited | 0 | 1 | 13 | 55 | 0 | 85 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| tourist_id | 0 | 1 | FALSE | 5000 | 1: 1, 2: 1, 3: 1, 4: 1 |
| route_id | 0 | 1 | FALSE | 5000 | 100: 1, 200: 1, 300: 1, 400: 1 |
Variable type: logical
| skim_variable | n_missing | complete_rate | mean | count |
|---|---|---|---|---|
| accessibility | 0 | 1 | 0.49 | FAL: 2531, TRU: 2469 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| age | 0 | 1 | 43.89 | 15.27 | 18.0 | 30.00 | 44.00 | 57.00 | 70 | ▇▇▇▇▇ |
| preferred_tour_duration | 0 | 1 | 5.52 | 1.71 | 3.0 | 4.00 | 6.00 | 7.00 | 8 | ▇▃▅▅▅ |
| tour_duration | 0 | 1 | 3.98 | 2.00 | 1.0 | 2.00 | 4.00 | 5.00 | 9 | ▆▇▃▅▁ |
| tourist_rating | 0 | 1 | 3.00 | 1.16 | 1.0 | 2.00 | 3.00 | 4.00 | 5 | ▇▇▇▇▇ |
| system_response_time | 0 | 1 | 3.24 | 1.02 | 1.5 | 2.36 | 3.23 | 4.13 | 5 | ▇▇▇▇▇ |
| recommendation_accuracy | 0 | 1 | 92.49 | 4.62 | 85.0 | 88.00 | 92.00 | 97.00 | 100 | ▇▆▆▆▆ |
| vr_experience_quality | 0 | 1 | 4.49 | 0.29 | 4.0 | 4.20 | 4.50 | 4.70 | 5 | ▇▆▆▆▅ |
| satisfaction | 0 | 1 | 3.53 | 0.74 | 3.0 | 3.00 | 3.00 | 4.00 | 5 | ▇▁▃▁▂ |
ℹ️ skimr muestra:
Tipo de variable. - Número de valores únicos.
Media y desviación estándar para numéricas.
Histograma en miniatura de distribución.
✍️ Nota técnica:
La media de satisfaction es ≈ 3.53, dentro de una escala de 1 a 5.
La variable tourist_rating tiene moda en valor 4.
Las variables
accessibility, site_name, route_id, etc., tienen valores repetidos y claramente categóricos.
📝 Interpretación técnica:
- No se detectan valores perdidos (NA) en el dataset, lo que permite aplicar análisis sin necesidad de imputación ni eliminación de registros.
datos_expandido <- datos %>%
mutate(interests = str_replace_all(interests, "\\[|\\]|‘|’|'|\"", "")) %>%
separate_rows(interests, sep = ",\\s*") %>%
mutate(interests = str_trim(interests)) %>%
mutate(grupo = ifelse(accessibility, "Accesibilidad", "Sin Accesibilidad"))
head(datos_expandido, 5) # mostra las primeras 5 filas del dataframe
⛔ Aclaración metodológica:
- La columna interests fue descompuesta mediante separate_rows() para representar cada tipo de interés como una fila independiente. Esto permitió obtener frecuencias limpias y realizar análisis por categoría individual.
⚠️ Es importante destacar que este dataset expandido (datos_expandido) no debe usarse para análisis globales, ya que un mismo turista aparece múltiples veces. Se usará solo en análisis cruzados por tipo de interés.
- Esta misma estrategia se aplicará más adelante asites_visited,que también contiene listas multivaluadas.
ℹ️ Puedes pulsar el ícono de “flecha ▶️” en la esquina superior derecha de la tabla HTML para explorarlas.
tabla_interes_grupo <- datos_expandido %>%
group_by(grupo, interests) %>%
summarise(
media_satisfaccion = round(mean(satisfaction, na.rm = TRUE), 2),
n = n()
) %>%
arrange(interests, desc(grupo))
kable(tabla_interes_grupo, caption = "Satisfacción media por grupo y tipo de interés") %>%
kable_styling(full_width = FALSE)
| grupo | interests | media_satisfaccion | n |
|---|---|---|---|
| Sin Accesibilidad | Architecture | 3.54 | 1001 |
| Accesibilidad | Architecture | 3.54 | 967 |
| Sin Accesibilidad | Art | 3.54 | 1039 |
| Accesibilidad | Art | 3.51 | 965 |
| Sin Accesibilidad | Cultural | 3.51 | 981 |
| Accesibilidad | Cultural | 3.51 | 998 |
| Sin Accesibilidad | History | 3.54 | 971 |
| Accesibilidad | History | 3.53 | 984 |
| Sin Accesibilidad | Nature | 3.56 | 1003 |
| Accesibilidad | Nature | 3.54 | 1018 |
head(tabla_interes_grupo, 5) # mostra las primeras 5 filas del dataframe
tabla_interes_satisfaccion <- datos_expandido %>%
group_by(interests) %>%
summarise(
media_satisfaccion = round(mean(satisfaction, na.rm = TRUE), 2),
n = n()
) %>%
arrange(desc(media_satisfaccion))
kable(tabla_interes_satisfaccion, caption = "Media de satisfacción por interés turístico") %>%
kable_styling(full_width = FALSE)
| interests | media_satisfaccion | n |
|---|---|---|
| Nature | 3.55 | 2021 |
| Architecture | 3.54 | 1968 |
| History | 3.53 | 1955 |
| Art | 3.52 | 2004 |
| Cultural | 3.51 | 1979 |
head(tabla_interes_satisfaccion, 5) # mostra las primeras 5 filas del dataframe
# Descomponer los sitios visitados
sitios_limpios <- datos %>%
mutate(sites_visited = str_replace_all(sites_visited, "\\[|\\]|‘|’|'|\"", "")) %>%
separate_rows(sites_visited, sep = ",\\s*") %>%
mutate(sites_visited = str_trim(sites_visited)) %>%
count(sites_visited, sort = TRUE)
head(sitios_limpios, 5) # presenta las primeras 5 filas del dataframe
ℹ️ La función count(sites_visited, sort = TRUE) es una otra manera de agrupar los valores para evitar duplicar los sitios visitados en los conteos.Aunque cada turista se repita en varias filas (una por cada sitio),
count()agrupa por nombre del sitio (sites_visited) y cuenta cuántas veces aparece cada uno.
kable(intereses_limpios, caption = "Frecuencia de intereses turísticos (limpios)") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
| interests | n |
|---|---|
| Nature | 2021 |
| Art | 2004 |
| Cultural | 1979 |
| Architecture | 1968 |
| History | 1955 |
Matriz de Correlación con la librería ggcorrplot
# Crear matriz de correlación
datos_numeric <- datos %>% select_if(is.numeric)
cor_matrix <- cor(datos_numeric, use = "complete.obs")
# Gráfico con ajustes visuales
ggcorrplot(cor_matrix,
hc.order = TRUE,
type = "lower",
lab = TRUE,
lab_size = 4, # Aumentar tamaño texto dentro
colors = c("red", "white", "darkgreen"),
title = "Mapa de calor de correlaciones (variables numéricas)",
ggtheme = theme_minimal(),
tl.cex = 10, # Tamaño etiquetas en ejes
#tl.srt = 0, # Etiquetas horizontales
tl.col = "black") # Color de etiquetas
# Guardar la gráfica en archivo PNG
ggsave("matriz-de-correlacion-ggcorrplot.png", width = 8, height = 5, dpi = 300)
Matriz de Correlación con la librería corrplot
# Seleccionar variables numéricas
datos_numeric <- datos %>% select_if(is.numeric)
# Calcular matriz de correlación
cor_matrix <- cor(datos_numeric, use = "complete.obs")
# Crear mapa de calor con corrplot
corrplot(cor_matrix,
method = "color",
type = "lower",
order = "hclust", # ordena por similitud
tl.col = "black", # color etiquetas
tl.cex = 0.8, # tamaño etiquetas
tl.srt = 45, # inclinación
addCoef.col = "black", # valores de correlación
col = colorRampPalette(c("red", "white", "green"))(200),
title = "Mapa de calor de correlaciones (corrplot)",
mar = c(1, 1, 1, 1)) # márgenes
ℹ️ Nota: Matriz de Correlación
tourist_ratingysatisfactiontienen una alta correlación positiva (≈ 0.85).El resto de variables tienen correlaciones débiles o moderadas.
tourist_ratingAnalizando la distribución de la valoración del turista, que es una variable ordinal. Esta variable será tratada como numérica en análisis inferencial, pero visualizadas como factores para claridad gráfica.
# Convertir a factor ordenado para evitar barras fuera de orden
ggplot(datos, aes(x = factor(round(tourist_rating, 1)))) +
geom_bar(fill = "skyblue", width = 0.85) +
labs(title = "Distribución de Tourist Rating",
x = "Tourist Rating (redondeado)",
y = "Frecuencia") +
theme_minimal() +
theme(
axis.text.x = element_text(size = 10, angle = 45, hjust = 0), # Aumentar tamaño eje x
axis.text.y = element_text(size = 12), # Aumentar tamaño eje y
axis.title = element_text(size = 14),
plot.title = element_text(size = 16, face = "bold")
)
# Guardar la gráfica en archivo PNG
ggsave("grafico-tourist-rating.png", width = 8, height = 5, dpi = 300)
vr_experience_qualityAnalizando la distribución de la calidad de la experiencia VR, que también es una variable ordinal. Esta variable será tratada como numérica en análisis inferencial, pero visualizadas como factores para claridad gráfica.
# Gráfico de barras de vr_experience_quality
ggplot(datos, aes(x = factor(vr_experience_quality))) +
geom_bar(fill = "orange") +
labs(title = "Distribución de Calidad de la Experiencia VR", x = "VR Quality", y = "Frecuencia")
# Guardar la gráfica en archivo PNG
ggsave("grafico-barras-vr_experience_quality.png", width = 8, height = 5, dpi = 300)
recommendation_accuracyAnalizando la distribución de la precisión del sistema de recomendación, que es una variable continua.
ggplot(datos, aes(x = recommendation_accuracy)) +
geom_histogram(binwidth = 5, fill = "darkgreen", color = "white") +
labs(title = "Precisión del sistema de recomendación", x = "Porcentaje (%)", y = "Frecuencia")
# Guardar la gráfica en archivo PNG
ggsave("grafico-barras-recommendation_accuracy.png", width = 8, height = 5, dpi = 300)
📝 Interpretación técnica:
Las variables
tourist_rating,vr_experience_qualityyrecommendation_accuracymuestran distribuciones razonablemente centradas hacia valores altos.Esto respalda la percepción general de buena experiencia, aunque los valores de satisfaction todavía requieren análisis más profundo.
interestsggplot(intereses_limpios, aes(x = reorder(interests, n), y = n)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = "Frecuencia de intereses turísticos", x = "Interés", y = "Frecuencia")
# Guardar la gráfica en archivo PNG
ggsave("grafico-barras-horizontales-interests.png", width = 8, height = 5, dpi = 300)
📝 Interpretación técnica:
- Los intereses más frecuentes son:
NatureyArt.- Esto refuerza que la mayoría de turistas se orientan hacia experiencias naturales y artísticas.
Analizar si hay diferencias en la satisfacción media según el tipo de interés turístico y según el grupo (con/sin accesibilidad).
ggplot(tabla_interes_satisfaccion, aes(x = reorder(interests, media_satisfaccion), y = media_satisfaccion)) +
geom_col(fill = "forestgreen") +
coord_flip() +
labs(title = "Satisfacción media por tipo de interés", x = "Interés", y = "Media de satisfacción")
# Guardar la gráfica en archivo PNG
ggsave("grafico-satisfacion-media-interes.png", width = 8, height = 5, dpi = 300)
📝 Interpretación técnica:
- Algunos intereses como
NatureyArchitecturetienen mayor nivel medio de satisfacción.- Al comparar grupos, no se observan grandes diferencias de satisfacción por interés entre personas con y sin accesibilidad, pero algunas categorías muestran pequeñas variaciones que podrían explorarse más.
- Este análisis abre la puerta a un modelo explicativo más profundo (ANOVA o regresión), aunque aquí nos centramos en descripción y comparación básica.
# Crear rangos de edad y ubicar grupo_edad justo después de age
datos <- datos %>%
mutate(grupo_edad = case_when(
age < 25 ~ "Menores de 25",
age >= 25 & age < 35 ~ "25-34",
age >= 35 & age < 45 ~ "35-44",
age >= 45 & age < 60 ~ "45-59",
age >= 60 ~ "60+",
TRUE ~ "No Especificado"
)) %>%
relocate(grupo_edad, .after = age) # <- Mueve grupo_edad después de age
# Ver las primeras filas
head(datos, 10)
# Resumen estadístico por grupo de edad
datos %>%
group_by(grupo_edad) %>%
summarise(
media_satisfaccion = round(mean(satisfaction, na.rm = TRUE), 2),
n = n()
) %>%
arrange(desc(media_satisfaccion)) %>%
kable(caption = "Satisfacción media por grupo de edad") %>%
kable_styling(full_width = FALSE)
| grupo_edad | media_satisfaccion | n |
|---|---|---|
| 45-59 | 3.57 | 1407 |
| Menores de 25 | 3.55 | 635 |
| 60+ | 3.54 | 1029 |
| 35-44 | 3.51 | 932 |
| 25-34 | 3.49 | 997 |
📝 Interpretación técnica:
- El grupo de edad con mayor satisfacción media es el de 45 a 59 años (3.57), seguido por Menores de 25 (3.55) y 60+ (3.54).
- Aunque la diferencia entre grupos es pequeña (menos de 0.1 puntos en una escala de 1 a 5), se observa una tendencia leve de mayor satisfacción en edades intermedias y mayores.
- Se observa un valor menor de satisfacción en las franjas etaarias de 25-34 años donde se puede personalizar las ofertas turísticas priorizando los segmentos como este, con menor nivel medio de satisfacción.
# Agrupar por interés y accesibilidad
tabla_interes_grupo <- datos_expandido %>%
group_by(interests, grupo) %>%
summarise(media_satisfaccion = round(mean(satisfaction), 2), .groups = "drop")
# Gráfico comparativo sin limitar eje
ggplot(tabla_interes_grupo, aes(x = media_satisfaccion, y = reorder(interests, media_satisfaccion), fill = grupo)) +
geom_col(position = "dodge") +
labs(title = "Satisfacción media por tipo de interés y accesibilidad",
x = "Media de satisfacción",
y = "Tipo de interés") +
theme_minimal() +
theme(axis.text = element_text(size = 10),
legend.position = "right")
# Guardar la gráfica en archivo PNG
ggsave("grafica-doblebarras-compartivo-accesibilidad.png", width = 8, height = 5, dpi = 300)
📝 Interpretación técnica:
- La satisfacción media por tipo de interés se mantiene en un rango estrecho entre ~3.52 y ~3.55 en todos los casos, lo que indica experiencias positivas estables independientemente del tipo de interés turístico.
- Los intereses que muestran ligeramente mayor satisfacción (para ambos grupos) son:
NatureyArchitecture- Las diferencias entre los grupos de Accesibilidad y Sin Accesibilidad son mínimas (inferiores a 0.02), lo cual no indica una brecha significativa en la experiencia por esta variable.
- Esto sugiere que las condiciones de accesibilidad están bien resueltas en los contextos turísticos estudiados, al menos en relación con el tipo de interés.
- Sin embargo, se podrían explorar más a fondo mediante un modelo explicativo (ej. regresión) si se desea identificar efectos combinados (interés × accesibilidad).
# Seleccionar los 10 sitios más visitados
sitios_top10 <- sitios_limpios %>%
top_n(10, n) %>%
arrange(desc(n))
# Gráfico de barras horizontales con etiquetas
ggplot(sitios_top10, aes(x = n, y = reorder(sites_visited, n))) +
geom_col(fill = "skyblue") +
geom_text(aes(label = n), hjust = -0.1, size = 3) +
labs(
title = "Top 10 sitios turísticos más visitados",
x = "Frecuencia de visitas",
y = "Sitio turístico"
) +
theme_minimal() +
theme(axis.text = element_text(size = 10)) +
xlim(0, max(sitios_top10$n) * 1.1) # Espacio para etiquetas
# Guardar la gráfica en archivo PNG
ggsave("grafica-top-10-sitios-visitados.png", width = 8, height = 5, dpi = 300)
📝 Interpretación técnica:
- El gráfico revela una clara preferencia por sitios emblemáticos de alto reconocimiento internacional, liderados por la Eiffel Tower (2072 visitas), Taj Mahal (2012) y el Colosseum (1994).
- Todos los sitios del top 10 superan las 1900 visitas, lo que indica una alta concentración de interés turístico en íconos culturales y arquitectónicos.
- La diferencia entre el primer y el décimo lugar es relativamente pequeña, lo que sugiere que la demanda está distribuida de forma bastante equilibrada entre los sitios más famosos.
- Este patrón puede estar influido por:
- La inclusión recurrente de estos lugares en rutas turísticas populares.
- La alta visibilidad digital (presencia en guías, redes sociales, plataformas de reservas).
- Su accesibilidad o fama como “destinos imperdibles”.
Esta es la primera pregunta de investigación y esta descripta en la Parte 1 del informe.
Se analiza la satisfacción general de los turistas utilizando una escala de 1 a 5. Se desea verificar si la media poblacional supera el umbral de 3 puntos, valor considerado como referencia para experiencias positivas en este dataset.
El valor central de una escala Likert de 1 a 5 se calcula como:
\[ \mu_0 = \frac{1 + 5}{2} = 3 \]
Se aplicará una prueba t de una muestra, que evalúa si la media
muestral difiere significativamente de un valor de referencia
(μ₀ = 3).
Hipótesis:
> - H₀: μ ≤ 3 (la satisfacción media no supera
3)
> - H₁: μ > 3 (la satisfacción media es superior a 3)
summary(datos$satisfaction)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 3.000 3.000 3.000 3.534 4.000 5.000
ggplot(datos, aes(x = satisfaction)) +
geom_histogram(bins = 15, fill = "steelblue", color = "white") +
geom_vline(xintercept = 3, linetype = "dashed", color = "red") +
labs(title = "Distribución de la satisfacción turística", x = "Satisfacción", y = "Frecuencia")
# Guardar la gráfica en archivo PNG
ggsave("histograma-satisfaccion.png", width = 8, height = 5, dpi = 300)
ℹ️ Nota:
La mayoría de valores están entre 3 y 4.
La distribución está sesgada hacia valores bajos.
La media de satisfacción es de aproximadamente 3.53, con una desviación estándar de 1.12.
# Test t de una muestra para verificar si la media de satisfacción supera 3
test_una_muestra <- t.test(datos$satisfaction, mu = 3, alternative = "greater")
# Mostramos el resultado completo
test_una_muestra
##
## One Sample t-test
##
## data: datos$satisfaction
## t = 51.321, df = 4999, p-value < 0.00000000000000022
## alternative hypothesis: true mean is greater than 3
## 95 percent confidence interval:
## 3.517076 Inf
## sample estimates:
## mean of x
## 3.5342
📝 Interpretación técnica:
- El test t muestra que la media observada es 3.53.
- El p-valor es 0, por lo tanto se rechaza la hipótesis nula H₀.
Existe evidencia estadística suficiente para afirmar que la satisfacción media supera el valor de referencia de 3.
Esta es la segunda pregunta de investigación y esta descripta en la Parte 2 del informe.
Se analiza si hay diferencias significativas entre turistas con
necesidades de accesibilidad frente a quienes no requieren
accesibilidad. Se comparan los valores medios de satisfacción entre
estos dos grupos.
Hipótesis:
> - H₀: μ₁ = μ₂ (no hay diferencia entre ambos
grupos)
> - H₁: μ₁ ≠ μ₂ (existe diferencia significativa)
Preparación de los grupos
datos <- datos %>%
mutate(grupo = ifelse(accessibility, "Accesibilidad", "Sin Accesibilidad"))
datos %>%
group_by(grupo) %>%
summarise(media = mean(satisfaction), sd = sd(satisfaction), n = n())
ggplot(datos, aes(x = grupo, y = satisfaction, fill = grupo)) +
geom_boxplot() +
labs(title = "Satisfacción por accesibilidad", x = "Grupo", y = "Satisfacción")
# Guardar la gráfica en archivo PNG
ggsave("boxplot-satisfaccion.png", width = 8, height = 5, dpi = 300)
ℹ️ Ambos grupos muestran medias similares, y rangos cercanos. No hay indicios gráficos de diferencia significativa.
# Ejecutar prueba y guardar p-valor
test_dos_grupos <- t.test(satisfaction ~ grupo, data = datos)
p_valor <- signif(test_dos_grupos$p.value, 4)
media_acces <- round(mean(datos$satisfaction[datos$grupo == "Accesibilidad"]), 3)
media_sin <- round(mean(datos$satisfaction[datos$grupo == "Sin Accesibilidad"]), 3)
test_dos_grupos
##
## Welch Two Sample t-test
##
## data: satisfaction by grupo
## t = -0.65106, df = 4996.9, p-value = 0.515
## alternative hypothesis: true difference in means between group Accesibilidad and group Sin Accesibilidad is not equal to 0
## 95 percent confidence interval:
## -0.05436677 0.02725892
## sample estimates:
## mean in group Accesibilidad mean in group Sin Accesibilidad
## 3.527339 3.540893
📝 Interpretación técnica:
- El p-valor es 0.515, por lo tanto no se rechaza H₀.
- No hay diferencia significativa entre los grupos con y sin accesibilidad.
write_csv(datos, "tourism_dataset_5000_limpio.csv")
Este análisis inferencial abordó dos preguntas fundamentales sobre la experiencia turística simulada:
Para ello, se desarrolló un enfoque reproducible con R, estructurado en seis fases:
janitor y se
transformaron identificadores (tourist_id,
route_id) en factores.interests y
sites_visited fueron descompuestas en
filas para obtener conteos precisos por tipo de interés y sitio
turístico.skimr y gráficos con
ggplot2.grupo_edad para análisis etario,
ordenada lógicamente.satisfaction,
tourist_rating, vr_experience_quality) se
trataron como cuasi-continuas para facilitar análisis paramétrico,
siguiendo criterios justificados en la literatura académica (Boone & Boone,
2012).Prueba t de una muestra (media de satisfacción):
Prueba t para dos grupos independientes (accesibilidad):
ggcorrplot y
corrplot.tourist_rating muestra correlación
fuerte con satisfaction (≈ 0.85).vr_experience_quality)
y la precisión del sistema de recomendación
(recommendation_accuracy) también presentan correlaciones
positivas aunque más débiles.Por grupo de edad:
Por tipo de interés turístico:
Nature, Architecture y
History muestran mayor media de satisfacción.Sitios turísticos más visitados:
ℹ️ Para obtener mayores informaciones sobre este proyecto clique aquí y será redireccionado a la página de GitHub del curso.
Boone, H. N., & Boone, D. A. (2012). Analyzing Likert Data. Journal of Extension.
Joshi, A., Kale, S., Chandel, S., & Pal, D. K. (2015). Likert Scale: Explored and Explained. British Journal of Applied Science & Technology.
Montgomery, D. C., & Runger, G. C. (2014). Probabilidad y estadística aplicada a la ingeniería. Wiley.
Cultural Tourism Dataset. (2022). Kaggle.